🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / data / src / commonMain / kotlin / org / meshtastic / core / data / manager / SessionManagerImpl.kt
Displaying Raw • Download
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/SessionManagerImpl.kt 3ca87fa0323214c7204c9d37acfe9d4b6ab6ea8b (3ca87fa0) Text, 4.80 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.data.manager
Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.atomicfu.atomic
Tff7b72import T7ee787kotlinx.atomicfu.update
Tff7b72import T7ee787kotlinx.collections.immutable.PersistentMap
Tff7b72import T7ee787kotlinx.collections.immutable.persistentMapOf
Tff7b72import T7ee787kotlinx.coroutines.delay
Tff7b72import T7ee787kotlinx.coroutines.flow.Flow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableSharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.asFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.distinctUntilChanged
Tff7b72import T7ee787kotlinx.coroutines.flow.filter
Tff7b72import T7ee787kotlinx.coroutines.flow.flow
Tff7b72import T7ee787kotlinx.coroutines.flow.flowOf
Tff7b72import T7ee787kotlinx.coroutines.flow.map
Tff7b72import T7ee787kotlinx.coroutines.flow.merge
Tff7b72import T7ee787okio.ByteString
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.model.SessionStatus
Tff7b72import T7ee787org.meshtastic.core.repository.SessionManager
Tff7b72import T7ee787kotlin.time.Clock
Tff7b72import T7ee787kotlin.time.Duration.Companion.seconds
Tff7b72import T7ee787kotlin.time.Instant
T8b949e/**
* In-memory implementation of [SessionManager] backed by an atomicfu-protected [PersistentMap].
*
* Per-node state replaces the single global passkey atomic that previously lived in `CommandSenderImpl`. Without this,
* bouncing remote-admin between two nodes within the firmware's 300 s TTL silently invalidated the first node's session
* because its passkey was overwritten by the second node's response.
*
* Threshold rationale (see `firmware/src/modules/AdminModule.cpp:1460-1481`):
* - Firmware TTL = 300 s, with passkey rotation at the 150 s halfway mark on the next response sent.
* - We treat 240 s as the "active enough to navigate without refreshing" boundary to leave headroom for in-flight
* packets, mesh latency, and clock skew. A user navigated into the remote-admin screen at 299 s would otherwise
* immediately time out on the next request.
*/
Tf0883e@Single
Tff7b72class T56d364SessionManagerImplTb4b4b4(Tff7b72private Tff7b72val Te6edf3clockTb4b4b4: Te6edf3ClockTb4b4b4) Tb4b4b4: Te6edf3SessionManager Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3entries Tff7b72= Te6edf3atomicTff7b72<Te6edf3PersistentMapTff7b72<Tffa657IntTb4b4b4, Te6edf3SessionEntryTff7b72>Tff7b72>Tb4b4b4(Te6edf3persistentMapOfTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3refreshFlow Tff7b72= Te6edf3MutableSharedFlowTff7b72<Tffa657IntTff7b72>Tb4b4b4(Te6edf3extraBufferCapacity Tff7b72= Te6edf3REFRESH_BUFFERTb4b4b4)
Tff7b72override Tff7b72val Te6edf3sessionRefreshFlowTb4b4b4: Te6edf3FlowTff7b72<Tffa657IntTff7b72> Tff7b72= Te6edf3refreshFlowTb4b4b4.Te6edf3asFlowTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffrecordSessionTb4b4b4(Te6edf3srcNodeNumTb4b4b4: Tffa657IntTb4b4b4, Te6edf3passkeyTb4b4b4: Te6edf3ByteStringTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3passkeyTb4b4b4.Te6edf3size Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Tff7b72return
Tff7b72val Te6edf3now Tff7b72= Te6edf3clockTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4)
Te6edf3entriesTb4b4b4.Te6edf3update Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3putTb4b4b4(Te6edf3srcNodeNumTb4b4b4, Te6edf3SessionEntryTb4b4b4(Te6edf3passkeyTb4b4b4, Te6edf3nowTb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffRecorded session refresh from Tffd700$Te6edf3srcNodeNumTa5d6ff (Tffd700${Te6edf3passkeyTb4b4b4.Te6edf3sizeTffd700}Ta5d6ff bytes)Ta5d6ff" Tb4b4b4}
Te6edf3refreshFlowTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3srcNodeNumTb4b4b4)
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffgetPasskeyTb4b4b4(Te6edf3destNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3ByteString Tff7b72= Te6edf3entriesTb4b4b4.Te6edf3valueTff7b72[Te6edf3destNumTff7b72]Tff7b72?.Te6edf3passkey Tff7b72?: Te6edf3ByteStringTb4b4b4.Te6edf3EMPTY
Tff7b72override Tff7b72fun Td2a8ffclearAllTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3entriesTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffClearing Tffd700${Te6edf3entriesTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3sizeTffd700}Ta5d6ff session entriesTa5d6ff" Tb4b4b4}
Tb4b4b4}
Te6edf3entriesTb4b4b4.Te6edf3value Tff7b72= Te6edf3persistentMapOfTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffobserveSessionStatusTb4b4b4(Te6edf3destNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3SessionStatusTff7b72> Tff7b72= Te6edf3mergeTb4b4b4(
Te6edf3flowOfTb4b4b4(Tffa657UnitTb4b4b4)Tb4b4b4,
Te6edf3refreshFlowTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3destNum Tb4b4b4}Tb4b4b4.Te6edf3map Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3flow Tb4b4b4{
Tff7b72while Tb4b4b4(Tff7b72trueTb4b4b4) Tb4b4b4{
Te6edf3delayTb4b4b4(Te6edf3RECHECK_INTERVALTb4b4b4)
Te6edf3emitTb4b4b4(Tffa657UnitTb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4.Te6edf3map Tb4b4b4{ Te6edf3computeStatusTb4b4b4(Te6edf3destNumTb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3distinctUntilChangedTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffcomputeStatusTb4b4b4(Te6edf3destNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3SessionStatus Tb4b4b4{
Tff7b72val Te6edf3entry Tff7b72= Te6edf3entriesTb4b4b4.Te6edf3valueTff7b72[Te6edf3destNumTff7b72] Tff7b72?: Tff7b72return Te6edf3SessionStatusTb4b4b4.Te6edf3NoSession
Tff7b72val Te6edf3age Tff7b72= Te6edf3clockTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3entryTb4b4b4.Te6edf3refreshedAt
Tff7b72return Tff7b72if Tb4b4b4(Te6edf3age Tff7b72< Te6edf3ACTIVE_THRESHOLDTb4b4b4) Tb4b4b4{
Te6edf3SessionStatusTb4b4b4.Te6edf3ActiveTb4b4b4(Te6edf3entryTb4b4b4.Te6edf3refreshedAtTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3SessionStatusTb4b4b4.Te6edf3StaleTb4b4b4(Te6edf3entryTb4b4b4.Te6edf3refreshedAtTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72data Tff7b72class T56d364SessionEntryTb4b4b4(Tff7b72val Te6edf3passkeyTb4b4b4: Te6edf3ByteStringTb4b4b4, Tff7b72val Te6edf3refreshedAtTb4b4b4: Te6edf3InstantTb4b4b4)
Tff7b72companion Tff7b72object Tb4b4b4{
T8b949e/**
* "Active enough to navigate" window. Set below the firmware TTL (300 s) to leave room for packet flight time
* and clock skew so users don't get sent into a screen that immediately times out.
*/
Tff7b72val Te6edf3ACTIVE_THRESHOLD Tff7b72= T79c0ff2T79c0ff4T79c0ff0.Te6edf3seconds
T8b949e/** Re-emit interval for [observeSessionStatus] so the UI transitions Active → Stale without user input. */
Tff7b72val Te6edf3RECHECK_INTERVAL Tff7b72= T79c0ff6T79c0ff0.Te6edf3seconds
Tff7b72private Tff7b72const Tff7b72val Te6edf3REFRESH_BUFFER Tff7b72= T79c0ff8
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.08s